package winOrDie.GameEngine.RT.mp;
import winOrDie.graphics.maps.WoD_Map;
import winOrDie.graphics.maps.WoD_Map_Editor;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;



public class Mapping {

	//matriz del mapa
	private static Casilla[][] mapLayer;
	
	//vector de entradas/salidas al mapa
	private static int[] parSalidas_x_y = {10,0,29,0,0,8,39,8,0,19,39,19,10,27,29,27};
	
	//vector de zona de piratas
	private static int[] parPiratas_x_y = {5,4,6,4,7,4,8,4,31,4,32,4,33,4,34,4
								,5,5,6,5,7,5,32,5,33,5,34,5
								,5,22,6,22,7,22,32,22,33,22,34,22
								,5,23,6,23,7,23,8,23,31,23,32,23,33,23,34,23};
	
	//vector de zona de rescate
	private static int[] parRescate_x_y = {16,12,18,11,21,11,23,12
								,16,15,18,16,21,16,23,15};

	private int columnas;
	private int filas;
	
	public static Casilla[][] getMapLayer() {
		return mapLayer;
	}

	public static class RT_GAME_STATE
	{
		public static final int STARTED = 0;
		public static final int PRINCESS_OUT = 1;
		public static final int CAPTAIN_WINS = 2;
		public static final int CAPTAIN_CAPTURED = 3;
		
	}
	
	/**
	 * 0 -> empezada, 1 -> princesa rescatada de la caseta, 2 -> capitan & princesa ganan, 3 -> piratas ganan
	 * @param estado
	 */
	private static int estadoPartida = 0;
	
	/**
	 * 0 -> empezada, 1 -> princesa rescatada de la caseta, 2 -> capitan & princesa ganan, 3 -> piratas ganan
	 */
	public int getEstadoPartida() {
		return estadoPartida;
	}
	
	/**
	 * 0 -> empezada, 1 -> princesa rescatada de la caseta, 2 -> capitan & princesa ganan, 3 -> piratas ganan
	 */
	public void setEstadoPartida(int estadoPartida) {
		this.estadoPartida = estadoPartida;
	}

	public void set_Estado_Partida(int estadoPartida)
	{
		this.estadoPartida = estadoPartida;
	}
	
	/**
	 * recive el nombre del archivo que contiene el codigo del mapa, el numero de filas y columnas
	 * y si el mapa debe estar o no explorado desde un principio
	 * y crea la matriz con las caracter�sticas de cada casilla del mapa
	 * @param x columnas del mapa/matriz
	 * @param y filas del mapa/matriz
	 * @param mapName nombre del archivo que contiene el mapa
	 * @param explorado true para que las casillas del mapa empiecen ya exploradas (tipo pirata)
	 */
	public Mapping(int x, int y, String mapName, boolean explorado) throws Exception{
		columnas = x;
		filas = y;
		mapLayer = new Casilla[x][y];
		
		// carga la cadena de strings del fichero mapName en charMap
//		String charMap = openMap(mapName);
//
//		System.out.println(charMap);

		//carga el mapa del xml
		WoD_Map woDMap = null;
		int valCeldaXml = 0;
		
    	try {
    		woDMap = new WoD_Map();
			woDMap = WoD_Map_Editor.get_WoD_Map_From_File("maps/" + mapName);
//    		woDMap = WoD_Map_Editor.get_WoD_Map_From_File("maps/jennytheflays.xml");
//			fileName = "maps/" + mapName; 
//			this.woDMap = woDMap;
		} catch (Exception e) {
			e.printStackTrace();
//			show_MessageBoxError(e.getMessage());
			return;
		}
		
		// filas
		for (int i = 0; i < y; i++) {
			
//			System.out.print("fila " + i);
			// columnas
			for (int j = 0; j < x; j++) {
//				System.out.print(" " + woDMap.getMapBoard().get_Map_Cell(j, i).getType() + " ");
//
//				// se guarda la letra/codigo en la Casilla que le pertoca
//				//mapLayer[j][i] = charMap.charAt(j + i*y);
//				entraValorCasilla(j, i, charMap.charAt(j + i*x), explorado);
				
				/*
				 * 1 pared
				 * 0 pasillo
				 * T torre de la doncella
				 * R zona de rescate de doncella
				 * I zona de inicio de partida para el bueno
				 * P zona de inicio de partida para los piratas
				 */
				switch (woDMap.getMapBoard().get_Map_Cell(j, i).getType()) {
				case 0:
					entraValorCasilla(j, i, '0', explorado);
					break;

				case 1:
					entraValorCasilla(j, i, '1', explorado);
					break;

				case 2:
					entraValorCasilla(j, i, 'P', explorado);
					break;

				case 3:
					entraValorCasilla(j, i, 'T', explorado);
					break;

				case 4:
					entraValorCasilla(j, i, 'R', explorado);
					break;

				case 5:
					entraValorCasilla(j, i, 'I', explorado);
					break;

				default:
					break;
				}

			}
			System.out.println(" ");
		}
		
	}

	/**
	 * introduce el tipo de la Casilla de la posicion [x][y]
	 * @param x columna
	 * @param y fila
	 * @param letra tipo de la casilla
	 * @param explorada condicion de explorada
	 */
	public void entraValorCasilla(int x, int y, char letra, boolean explorada){
		//mapLayer[x][y] = letra;
		//mapLayer[x][y].modificarTipoCasilla(letra);
		mapLayer[x][y] = new Casilla(letra, explorada);
		
	}
	
	/**
	 * devuelve el valor char de la Casilla [x][y]
	 * @param x columna
	 * @param y fila
	 * @return
	 */
	public char leerValor(int x, int y){
		//return mapLayer[x][y];
		return mapLayer[x][y].leerTipoCasilla();
	}
	
	/**
	 * Devuelve la cantidad de vision en la casilla indicada
	 * @param x columna
	 * @param y fila
	 * @return 0 -> nada, 100 -> vision total
	 */
	public int Vision(int x, int y){
		return mapLayer[x][y].leerVisionCasilla();
	}
	
	public void eplorarMapaEntero(){
		
		//filas
		for (int i = 0; i < this.filas; i++) {
			//columnas
			for (int j = 0; j < this.columnas; j++) {
				explorarCasilla(j, i);
			}
		}
		
	}
	
	/**
	 * Recive el nombre de un mapa y devuelve una cadena de string con su contenido
	 * devuelve "error" si ha fallado
	 * @param name
	 * @return
	 */
	public String openMap(String name){
		// carga archivo
		File archivo = new File("maps/" + name);
		FileReader fr;
		BufferedReader br;
		String map = "";
		String mapLine;
		
		try {
			fr = new FileReader (archivo);
			br = new BufferedReader(fr);

			// lee archivo por lineas y lo guarda en map
			while(((mapLine = br.readLine()) != null)) map = map + mapLine;
			
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			map = "error";
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			map = "error";
			e.printStackTrace();
		}
		
		
		return map;
	}
	
	public int getColumnas() {
		return columnas;
	}

	public void setColumnas(int columnas) {
		this.columnas = columnas;
	}

	public int getFilas() {
		return filas;
	}

	public void setFilas(int filas) {
		this.filas = filas;
	}

	/**
	 * indica si es Casilla transitable o no
	 * 
	 * 1 pared
	 * 0 pasillo
	 * T torre de la doncella
	 * R zona de rescate de doncella
	 * I zona de inicio de partida para el bueno
	 * P zona de inicio de partida para los piratas
	 * 
	 * @param x columna
	 * @param y fila
	 * @return true si es pasillo
	 */
	public boolean pasillo(int x, int y){
		//return (mapLayer[x][y] == '0');
		if ((x < 0) || (y < 0) || !(y < filas) || !(x < columnas)) return false;
		if (mapLayer[x][y].leerTipoCasilla() == '0'
			|| mapLayer[x][y].leerTipoCasilla() == 'R'
			|| mapLayer[x][y].leerTipoCasilla() == 'I'
			|| mapLayer[x][y].leerTipoCasilla() == 'P') return true;
		return false;
		//return (mapLayer[x][y].leerTipoCasilla() == '0');
	}
	
	/**
	 * indica si la Casilla enviada corresponde a una del per�metro
	 * @param x -> columna
	 * @param y -> fila
	 * @return
	 */
	public boolean esBorde(int x, int y){
		return ((x == columnas - 1) || (x == 0) || (y == filas - 1) || (y == 0));
	}
	
	/**
	 * recive la posicion de una Casilla y la direccion, y devuelve la cantidad de casillas en esa direccion hasta la pared
	 * sin contar la propia Casilla
	 * @param x -> columna
	 * @param y -> fila
	 * @param direccion
	 * direcciones => U -> arriba / D -> abajo / L -> izquierda / R -> derecha
	 * @return
	 */
	public int longPasillo(int x, int y, char direccion){
		int pasos = 0;
		
		if (direccion == 'U')
			//arriba
			while (y > 0) {
				if (pasillo(x, y - 1)){
					pasos++;
					y--;
				}else return pasos;
			}			
			;
		if (direccion == 'D')
			//abajo
			while (getFilas() - 1 > y) {
				if (pasillo(x, y + 1)){
					pasos++;
					y++;
				}else return pasos;
			}			
			;
		if (direccion == 'L')
			//izquierda
			while (x > 0) {
				if (pasillo(x - 1, y)){
					pasos++;
					x--;
				}else return pasos;
			}
			;
		if (direccion == 'R') 
			/* derecha
			 * si vas hacia la derecha, mira si la siguiente Casilla es de pasillo,
			 * si lo es pasos++, te mueves a esa Casilla y vuelve a mirar si es de pasillo,
			 * asi hasta acercarte a la �ltima, no hace falta entrar en esta
			 * si encuentra una pared se acaba y devuelve el conteo de casillas del pasillo
			 */
				while (getColumnas() - 1 > x) {
					if (pasillo(x + 1, y)){
						pasos++;
						x++;
					}else return pasos;
				}
			;

			return pasos;
	}
	
	/**
	 * Recive la casilla donde esta y recorre la zona en todas direcciones a la distancia maxima indicada
	 * @param x -> columna
	 * @param y -> fila
	 * @param distancia de vision de radio
	 * @return true si se han recorrido casillas nuevas
	 */
	public boolean recorrerZona(int x, int y, int distancia){
		int cambios = 0;
		int xTemp = x;
		int yTemp = y;
		
		//exploro MI casilla... vaya tonteria pero bueno
		if (explorarCasilla(x, y)) cambios++;
		
		
		//arriba---------------------------------------------------
		
			//explora a partir de la casilla de encima hasta llegar a una pared (inslusive) o al m�ximo de distancia de vision
			if (explorarPalante(x, y-1, 'U', distancia)) cambios++;
			if ((explorarPeriferiaUpDownSup(x-1, y-1, distancia, -1)) != 0) cambios++;
			if ((explorarPeriferiaUpDownInf(x+1, y-1, distancia, -1)) != 0) cambios++;

			
		//abajo---------------------------------------------------
			x = xTemp;
			y = yTemp;
	
			//explora a partir de la casilla de debajo hasta llegar a una pared (inslusive) o al m�ximo de distancia de vision
			if (explorarPalante(x, y+1, 'D', distancia)) cambios++;
			if ((explorarPeriferiaUpDownSup(x-1, y+1, distancia, 1)) != 0) cambios++;
			if ((explorarPeriferiaUpDownInf(x+1, y+1, distancia, 1)) != 0) cambios++;
			

		//izquierda---------------------------------------------------
			x = xTemp;
			y = yTemp;
			
			//explora a partir de la casilla de su izquierda hasta llegar a una pared (inslusive) o al m�ximo de distancia de vision
			if (explorarPalante(x-1, y, 'L', distancia)) cambios++;
			if ((explorarPeriferiaLeftRightSup(x-1, y-1, distancia, -1)) != 0) cambios++;
			if ((explorarPeriferiaLeftRightInf(x-1, y+1, distancia, -1)) != 0) cambios++;
			

		//derecha---------------------------------------------------
			x = xTemp;
			y = yTemp;
			
			//explora a partir de la casilla de su derecha hasta llegar a una pared (inslusive) o al m�ximo de distancia de vision
			if (explorarPalante(x+1, y, 'R', distancia)) cambios++;
			if ((explorarPeriferiaLeftRightSup(x+1, y-1, distancia, 1)) != 0) cambios++;
			if ((explorarPeriferiaLeftRightInf(x+1, y+1, distancia, 1)) != 0) cambios++;
			
			
		return (cambios!=0);
	}
	
	/**
	 * explora recto hasta llegar a una pared (pared inclusive)
	 * devuelve si hay casillas exploradas que antes no lo estaban
	 * @param x -> columna
	 * @param y -> fila
	 * @param direccion => U -> arriba / D -> abajo / L -> izquierda / R -> derecha
	 * @param distancia -> rango de vision a explorar
	 * @return true si se han recorrido casillas nuevas
	 */
	private boolean explorarPalante(int x, int y, char direccion, int distancia){
		int cambios = 0;
		
		// explora la casilla actual, si no estaba recorrida de antes -> cambios++
		if (explorarCasilla(x, y)) cambios++;
		// reduce en uno el recorrido por la exploracion
		distancia--;
		
		// mientras sea pasillo Y aun haya rango
		while ((pasillo(x,y)) && (distancia > 0)){
			// segun sea la direccion cambia de casilla
			
			if (direccion == 'R') x++;
			if (direccion == 'L') x--;
			if (direccion == 'D') y++;
			if (direccion == 'U') y--;

			// explora la casilla actual, si no estaba recorrida de antes -> cambios++
			if (explorarCasilla(x, y)) cambios++;
			
			// reduce en uno el recorrido por la exploracion
			distancia--;
		}
		
		return (cambios != 0);
	}
	
	/**
	 * explora la diagonal superior para direcciones de izquierda o derecha
	 * @param x -> columna
	 * @param y -> fila
	 * @param distancia -> campo de vista
	 * @param direccion -> si = -1 es que el recorrido en tu eje principal de tu movimiento es negativo, si n = 1 es que es positivo
	 * (usea, si la direccion es de subida, el recorrido de las y es negativo, si es de bajada, el recorrido de las y es positivo)
	 * @return numero de casillas exploradas por primera vez
	 */
	private int explorarPeriferiaLeftRightSup(int x, int y, int distancia, int direccion){
		int cambios = 0;
		
		// lo primero es asegurar que tiene rango disponible de vision aun
		if (distancia > 0){
			// explora la casilla actual, si no estaba recorrida de antes -> cambios++
			if (explorarCasilla(x, y)) cambios++;
			distancia--;
			
			// comprobar de nuevo la distancia
			if (distancia > 0){
				// si era pasillo sigue al siguiente
				if (pasillo(x,y)){
					
					// pasa a la siguiente columna o fila segun la direccion
					x = x + (1 * direccion);
					
					// explora la casilla actual, si no estaba recorrida de antes -> cambios++
					if (explorarCasilla(x, y)) cambios++;
					distancia--;
					
					// si era pasillo sigue al siguiente
					if (pasillo(x,y)){
						// tira pa arriba para seguir con el proceso en diagonal
						explorarPeriferiaLeftRightSup(x, y-1, distancia, direccion);
						
						// una vez termina la escalada, tira hacia delante segun le toque
						if ((direccion < 0) && (distancia > 0))	if (explorarPalante(x-1, y, 'L', distancia)) cambios++;
						if ((direccion > 0) && (distancia > 0))	if (explorarPalante(x+1, y, 'R', distancia)) cambios++;
					}
				}
			}
		}
		return cambios;
	}
	
	/**
	 * explora la diagonal inferior para direcciones de izquierda o derecha
	 * @param x -> columna
	 * @param y -> fila
	 * @param distancia -> campo de vista
	 * @param direccion -> si = -1 es que el recorrido en tu eje principal de tu movimiento es negativo, si n = 1 es que es positivo
	 * (usea, si la direccion es de subida, el recorrido de las y es negativo, si es de bajada, el recorrido de las y es positivo)
	 * @return numero de casillas exploradas por primera vez
	 */
	private int explorarPeriferiaLeftRightInf(int x, int y, int distancia, int direccion){
		int cambios = 0;
		
		// lo primero es asegurar que tiene rango disponible de vision aun
		if (distancia > 0){
			// explora la casilla actual, si no estaba recorrida de antes -> cambios++
			if (explorarCasilla(x, y)) cambios++;
			distancia--;
			
			// comprobar de nuevo la distancia
			if (distancia > 0){
				// si era pasillo sigue al siguiente
				if (pasillo(x,y)){
					
					// pasa a la siguiente columna o fila segun la direccion
					x = x + (1 * direccion);
					
					// explora la casilla actual, si no estaba recorrida de antes -> cambios++
					if (explorarCasilla(x, y)) cambios++;
					distancia--;
					
					// si era pasillo sigue al siguiente
					if (pasillo(x,y)){
						// tira pa abajo para seguir con el proceso en diagonal
						explorarPeriferiaLeftRightInf(x, y+1, distancia, direccion);
						
						// una vez termina la escalada, tira hacia delante segun le toque
						if ((direccion < 0) && (distancia > 0))	if (explorarPalante(x-1, y, 'L', distancia)) cambios++;
						if ((direccion > 0) && (distancia > 0))	if (explorarPalante(x+1, y, 'R', distancia)) cambios++;
					}
				}
			}
		}
		return cambios;
	}
	
	/**
	 * explora la diagonal superior para direcciones de arriba o abajo
	 * @param x -> columna
	 * @param y -> fila
	 * @param distancia -> campo de vista
	 * @param direccion -> si = -1 es que el recorrido en tu eje principal de tu movimiento es negativo, si n = 1 es que es positivo
	 * (usea, si la direccion es de subida, el recorrido de las y es negativo, si es de bajada, el recorrido de las y es positivo)
	 * @return numero de casillas exploradas por primera vez
	 */
	private int explorarPeriferiaUpDownSup(int x, int y, int distancia, int direccion){
		int cambios = 0;
		
		// lo primero es asegurar que tiene rango disponible de vision aun
		if (distancia > 0){
			// explora la casilla actual, si no estaba recorrida de antes -> cambios++
			if (explorarCasilla(x, y)) cambios++;
			distancia--;
			
			// comprobar de nuevo la distancia
			if (distancia > 0){
				// si era pasillo sigue al siguiente
				if (pasillo(x,y)){
					
					// pasa a la siguiente columna o fila segun la direccion
					y = y + (1 * direccion);
					
					// explora la casilla actual, si no estaba recorrida de antes -> cambios++
					if (explorarCasilla(x, y)) cambios++;
					distancia--;
					
					// si era pasillo sigue al siguiente
					if (pasillo(x,y)){
						// tira pa abajo para seguir con el proceso en diagonal
						explorarPeriferiaUpDownSup(x-1, y, distancia, direccion);
						
						// una vez termina la escalada, tira hacia delante segun le toque
						if ((direccion < 0) && (distancia > 0))	if (explorarPalante(x, y-1, 'U', distancia)) cambios++;
						if ((direccion > 0) && (distancia > 0))	if (explorarPalante(x, y+1, 'D', distancia)) cambios++;
					}
				}
			}
		}
		return cambios;
	}

	/**
	 * explora la diagonal inferior para direcciones de arriba o abajo
	 * @param x -> columna
	 * @param y -> fila
	 * @param distancia -> campo de vista
	 * @param direccion -> si = -1 es que el recorrido en tu eje principal de tu movimiento es negativo, si n = 1 es que es positivo
	 * (usea, si la direccion es de subida, el recorrido de las y es negativo, si es de bajada, el recorrido de las y es positivo)
	 * @return numero de casillas exploradas por primera vez
	 */
	private int explorarPeriferiaUpDownInf(int x, int y, int distancia, int direccion){
		int cambios = 0;
		
		// lo primero es asegurar que tiene rango disponible de vision aun
		if (distancia > 0){
			// explora la casilla actual, si no estaba recorrida de antes -> cambios++
			if (explorarCasilla(x, y)) cambios++;
			distancia--;
			
			// comprobar de nuevo la distancia
			if (distancia > 0){
				// si era pasillo sigue al siguiente
				if (pasillo(x,y)){
					
					// pasa a la siguiente columna o fila segun la direccion
					y = y + (1 * direccion);
					
					// explora la casilla actual, si no estaba recorrida de antes -> cambios++
					if (explorarCasilla(x, y)) cambios++;
					distancia--;
					
					// si era pasillo sigue al siguiente
					if (pasillo(x,y)){
						// tira pa abajo para seguir con el proceso en diagonal
						explorarPeriferiaUpDownInf(x+1, y, distancia, direccion);
						
						// una vez termina la escalada, tira hacia delante segun le toque
						if ((direccion < 0) && (distancia > 0))	if (explorarPalante(x, y-1, 'U', distancia)) cambios++;
						if ((direccion > 0) && (distancia > 0))	if (explorarPalante(x, y+1, 'D', distancia)) cambios++;
					}
				}
			}
		}
		return cambios;
	}
	
	/**
	 * explora la casilla si puede, si la ha explorado devuelve true
	 * sino devuelve false, tanto puede ser porque ya lo estaba o porque no existe la casilla
	 * @param x columna
	 * @param y fila
	 * @return true si hay modificacion
	 */
	public boolean explorarCasilla(int x, int y){
		if (!(x<0) && !(y<0) && (y < filas) && (x < columnas)){
			return (mapLayer[x][y].casillaExplorada(true));
		}
		
		return false;
	}
	
 /**
 * funcion que devuelve una casilla aleatoria respecto a las entradas en el mapa
 * @param rol se ha de indicar el rol del personaje
 * @return int[2] con la entrada aleatoria en x,y
 */
	public static int[] entradaRandom(int rol){
		int x;
		int[] salida = new int[2];
		
		if (rol == 1){
			x = (int)(Math.random()*(parSalidas_x_y.length/2 - 1));
			salida[0] = parSalidas_x_y[x*2];
			salida[1] = parSalidas_x_y[x*2+1];
		}else{
			x = (int)(Math.random()*(parPiratas_x_y.length/2 - 1));
			salida[0] = parPiratas_x_y[x*2];
			salida[1] = parPiratas_x_y[x*2+1];
		}
		
		return salida;
		
	}
	
	/**
	 * 
	 * @param x casilla columna
	 * @param y casilla fila
	 * @return cierto si la casilla indicada es de zona de rescate de la doncella
	 */
	public boolean esZonaRescate(int x, int y){
		boolean salida = false;
		int i = 0;
		
		while ((parRescate_x_y.length/2 > i) && (!salida)) {
			if ((x == parRescate_x_y[i*2]) && (y == parRescate_x_y[i*2+1])) salida = true;
			i++;
		}

		return salida;
	}
	
	/**
	 * 
	 * @param x casilla columna
	 * @param y casilla fila
	 * @return cierto si la casilla indicada es de zona de salida del mapa
	 */
	public boolean esZonaSalida(int x, int y){
		boolean salida = false;
		int i = 0;
		
		while ((parSalidas_x_y.length/2 > i) && (!salida)) {
			if ((x == parSalidas_x_y[i*2]) && (y == parSalidas_x_y[i*2+1])) salida = true;
			i++;
		}

		return salida;
	}
	
}
